using System;
using System.Collections.Generic;
using System.Text;
using RideMe.Core.Properties;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace RideMe.Core
{
    // TODO: think about making this inherit from baseprocess.
    public class WebServerProcess : RubyProcess 
    {
        public WebServerProcess()
        {
        }

        private System.Threading.Timer _timer;

        public event EventHandler Started;

        /// <summary>
        /// Raises the started event.
        /// </summary>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnStarted(EventArgs e)
        {
            if (Started != null)
            {
                Started(this, e);
            }
        }

        private int _port;

        /// <summary>
        /// Gets or sets the port.
        /// </summary>
        /// <value>The port.</value>
        public int Port
        {
            get { return _port; }
            set { _port = value; }
        }

        private string _projectPath;

        /// <summary>
        /// Gets or sets the project path.
        /// </summary>
        /// <value>The project path.</value>
        public string ProjectPath
        {
            get { return _projectPath; }
            set { _projectPath = value; }
        }

        public bool _running;

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="T:WebServer"/> is running.
        /// </summary>
        /// <value><c>true</c> if running; otherwise, <c>false</c>.</value>
        public bool Running
        {
            get 
            {
                if (this.TargetProcess != null)
                {
                    return _running;
                }
                else
                {
                    return false;
                }
            }
            set { _running = value; }
        }

        /// <summary>
        /// Starts the server.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="port">The port.</param>
        public void StartServer(string path, int port)
        {
            _projectPath = path;

            _port = port;

            StartServer();
        }

        /// <summary>
        /// Starts the server.
        /// </summary>
        public void StartServer()
        {
            if (!Validator.ValidDirectory(_projectPath))
            {
                throw new ArgumentException(Resources.ProjectPathNotSetError);
            }

            if (_port == 0)
            {
                throw new ArgumentException(Resources.InvalidPortError);
            }

            string commandString = string.Format(@"'{0}\{1}' {2} {3}", _projectPath, Resources.ServerScriptPath, Resources.ServerArgs, _port);
            this.RunCommand(commandString, false);

            // This is a bit strange, but the first time the server is
            // is run, the events do not fire, no matter what I do. 
            // So we're going to wait 20 seconds, and if it's
            // not started we're just going to say it is. A bit of a hack?
            // Definitely. But until I find a better solution, this will have to do.
            _timer = new System.Threading.Timer(TimerElapsedCallback, null, 20000, 1000);

        }

        /// <summary>
        /// Stops the server.
        /// </summary>
        public void StopServer()
        {
            _running = false;

            if ((this.TargetProcess != null) && (!this.TargetProcess.HasExited))
            {
                this.TargetProcess.Kill();
                this.TargetProcess.Dispose();
            }

            this.TargetProcess = null;
        }

        /// <summary>
        /// Restarts the server.
        /// </summary>
        public void RestartServer()
        {
            StopServer();
            StartServer();
        }

        /// <summary>
        /// Callback method that fires when the timer due time is up.
        /// </summary>
        /// <param name="state"></param>
        private void TimerElapsedCallback(object state)
        {
            if (!_running)
            {
                _running = true;
                OnStarted(EventArgs.Empty);
            }
            _timer.Dispose();
        }

        /// <summary>
        /// Hanldes the receiving of outupt from the WebServer process.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnOutputReceived(DataReceivedEventArgs e)
        {
            base.OnOutputReceived(e);

            if (e.Data.Contains(Resources.WebServerStartedText))
            {
                _running = true;
                OnStarted(EventArgs.Empty);
            }
        }
    }
}
